<?php

namespace app\worker\library\kefu;

use ba\Random;
use Throwable;
use think\facade\Config;
use app\common\facade\Token;
use app\admin\model\kefu\User;

/**
 * 客服系统用户验权类
 */
class Auth
{
    /**
     * token 入库 type
     */
    public const TOKEN_TYPE = 'kefu_user';

    /**
     * Model实例
     * @var ?User
     */
    protected ?User $model = null;

    /**
     * 是否登录
     * @var bool
     */
    protected bool $loginEd = false;

    /**
     * 令牌
     * @var string
     */
    protected string $token = '';

    /**
     * 错误消息
     * @var string
     */
    protected string $error = '';

    /**
     * 游客令牌默认有效期
     * @var int
     */
    protected int $keepTime = 2592000;

    /**
     * 允许输出的字段
     * @var array
     */
    protected array $allowFields = ['id', 'identity', 'user_id', 'admin_id', 'nickname', 'status'];

    public function __construct(array $config = [])
    {
        if (isset($config['token_keep_time'])) {
            $this->setKeepTime((int)$config['token_keep_time']);
        }
    }

    /**
     * 初始化
     * @param array $options
     * @return Auth
     */
    public static function instance(array $options = []): Auth
    {
        $request = request();
        if (!isset($request->kfUserAuth)) {
            $request->kfUserAuth = new static($options);
        }
        return $request->kfUserAuth;
    }

    /**
     * 魔术方法-会员信息字段
     * @param $name
     * @return mixed 字段信息
     */
    public function __get($name): mixed
    {
        return $this->model?->$name;
    }

    /**
     * 根据Token初始化用户登录态
     * @param $token
     * @return bool
     * @throws Throwable
     */
    public function init($token): bool
    {
        $tokenData = Token::get($token);
        if ($tokenData) {
            $userId = intval($tokenData['user_id']);
            if ($tokenData['type'] == self::TOKEN_TYPE && $userId > 0) {
                $this->model = User::where('id', $userId)->find();
                if (!$this->model) {
                    $this->setError('Account not exist');
                    return false;
                }
                $this->token = $token;
                $this->loginSuccessful();
                return true;
            }
        }

        $this->setError('Token login failed');
        $this->reset();
        return false;
    }

    /**
     * 为一个客服系统用户绑定一个注册用户
     * @throws Throwable
     */
    public function kfUserBind(string $token, string $bindIdentity, int $bindId): bool
    {
        $this->init($token);
        if ($this->isLogin() && $this->identity == 'tourist') {
            User::where('id', $this->id)
                ->update([
                    $bindIdentity . '_id' => $bindId,
                    'identity'            => 'user',
                ]);

            // 更新 model 数据，重查走获取器等
            $this->model = User::where('id', $this->id)->find();
            return true;
        }
        return false;
    }

    /**
     * 获得客服系统用户
     * @param string $identity 用户身份
     * @param array  $extend   扩展数据
     * @throws Throwable
     */
    public function kfUser(string $identity, array $extend = []): void
    {
        // 扩展（绑定）注册用户ID
        $extendUserId = $extend['id'] ?? 0;

        if (in_array($identity, ['admin', 'user']) && $extendUserId) {
            $user = User::where($identity . '_id', $extendUserId)->find();

            if ($user) {
                $this->direct($user->id);
            }

            // 以注册用户身份登录失败，又存在游客 token
            if (!$this->isLogin() && !empty($extend['token'])) {
                $this->kfUserBind($extend['token'], $identity, $extendUserId);
            }
        } elseif ($identity == 'tourist' && !empty($extend['token'])) {
            $this->init($extend['token']);
        }

        if (!$this->isLogin()) {
            // 建立用户
            $nowTime = time();
            $user    = User::create([
                'identity'        => $identity == 'admin' ? 'user' : $identity,
                'user_id'         => $identity == 'user' ? $extendUserId : 0,
                'admin_id'        => $identity == 'admin' ? $extendUserId : 0,
                'referrer'        => request()->get('referrer', request()->header('origin')),
                'last_login_time' => $nowTime,
                'last_login_ip'   => request()->ip(),
                'join_time'       => $nowTime,
                'join_ip'         => request()->ip(),
                'status'          => 'offline',
            ]);
            $this->direct($user->id);
        }
    }

    /**
     * 直接登录用户账号
     * @param int $userId 用户ID
     * @return bool
     * @throws Throwable
     */
    public function direct(int $userId): bool
    {
        $this->model = User::find($userId);
        if (!$this->model) return false;
        if (Config::get('buildadmin.user_sso')) {
            Token::clear(self::TOKEN_TYPE, $this->model->id);
        }
        return $this->loginSuccessful();
    }

    /**
     * 登录成功
     * @return bool
     */
    public function loginSuccessful(): bool
    {
        if (!$this->model) {
            return false;
        }
        $this->model->startTrans();
        try {
            $this->model->last_login_time = time();
            $this->model->last_login_ip   = request()->ip();
            $this->model->save();
            $this->loginEd = true;

            if (!$this->token) {
                $this->token = Random::uuid();
                Token::set($this->token, self::TOKEN_TYPE, $this->model->id, $this->keepTime);
            }
            $this->model->commit();
        } catch (Throwable $e) {
            $this->model->rollback();
            $this->setError($e->getMessage());
            return false;
        }
        return true;
    }

    /**
     * 登录失败
     * @return bool
     */
    public function loginFailed(): bool
    {
        if (!$this->model) return false;
        $this->model->startTrans();
        try {
            $this->model->last_login_time = time();
            $this->model->last_login_ip   = request()->ip();
            $this->model->save();
            $this->model->commit();
        } catch (Throwable $e) {
            $this->model->rollback();
            $this->setError($e->getMessage());
            return false;
        }
        return $this->reset();
    }

    /**
     * 退出登录
     * @return bool
     */
    public function logout(): bool
    {
        if (!$this->loginEd) {
            $this->setError('You are not logged in');
            return false;
        }
        return $this->reset();
    }

    /**
     * 是否登录
     * @return bool
     */
    public function isLogin(): bool
    {
        return $this->loginEd;
    }

    /**
     * 获取会员模型
     * @return User
     */
    public function getUser(): User
    {
        return $this->model;
    }

    /**
     * 获取允许输出字段
     * @return array
     */
    public function getAllowFields(): array
    {
        return $this->allowFields;
    }

    /**
     * 设置允许输出字段
     * @param $fields
     * @return void
     */
    public function setAllowFields($fields): void
    {
        $this->allowFields = $fields;
    }

    /**
     * 获取会员信息
     * @return array
     */
    public function getUserInfo(): array
    {
        if (!$this->model) return [];
        $info          = $this->model->toArray();
        $info          = array_intersect_key($info, array_flip($this->getAllowFields()));
        $info['token'] = $this->getToken();

        if ($info['identity'] == 'csr') {
            $info['nickname'] = Common::trueValue([
                $info['nickname'],
                $this->model->admin?->nickname,
                $this->model->sysUser?->nickname,
            ]);

            $info['avatar'] = Common::trueValue([
                $this->model->admin?->avatar,
                $this->model->sysUser?->avatar,
                Config::get('buildadmin.default_avatar'),
            ]);
        }

        return $info;
    }

    /**
     * 获取用户Token
     * @return string
     */
    public function getToken(): string
    {
        return $this->token;
    }

    /**
     * 设置Token有效期
     * @param int $keepTime
     * @return void
     */
    public function setKeepTime(int $keepTime = 0): void
    {
        $this->keepTime = $keepTime;
    }

    /**
     * 设置错误消息
     * @param string $error
     * @return Auth
     */
    public function setError(string $error): Auth
    {
        $this->error = $error;
        return $this;
    }

    /**
     * 获取错误消息
     * @return string
     */
    public function getError(): string
    {
        return $this->error ? __($this->error) : '';
    }

    /**
     * 属性重置（注销、登录失败、重新初始化等将单例数据销毁）
     */
    protected function reset(bool $deleteToken = true): bool
    {
        if ($deleteToken && $this->token) {
            Token::delete($this->token);
        }

        $this->token   = '';
        $this->loginEd = false;
        $this->model   = null;
        $this->setError('');
        return true;
    }
}